package cn.minimelon.api.service.module.impl;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.io.IoUtil;
import cn.hutool.core.map.MapUtil;
import cn.hutool.core.util.ObjUtil;
import cn.hutool.core.util.StrUtil;
import cn.minimelon.api.constant.UserConstants;
import cn.minimelon.api.domain.enums.DataTypeEnum;
import cn.minimelon.api.domain.module.po.DataProp;
import cn.minimelon.api.domain.module.vo.*;
import cn.minimelon.api.mapper.module.DataPropMapper;
import cn.minimelon.api.service.module.IDataPropService;
import cn.minimelon.api.service.module.utils.PropUtils;
import cn.minimelon.api.utils.MelonUtils;
import lombok.extern.slf4j.Slf4j;
import org.apache.ibatis.solon.annotation.Db;
import org.noear.snack.ONode;
import org.noear.solon.Utils;
import org.noear.solon.annotation.Component;
import org.noear.solon.data.annotation.Transaction;

import java.io.InputStream;
import java.util.Date;
import java.util.List;
import java.util.Map;

@Slf4j
@Component
public class DataPropServiceImpl implements IDataPropService {
    @Db
    private DataPropMapper modelMapper;

    @Override
    public List<DataPropVO> selectList(DataPropVO dataProp) {
        List<DataPropVO> list = modelMapper.selectEntityList(dataProp);
        for (DataPropVO prop : list) {
            prop.setSwt(ONode.deserialize(prop.getSwtJson(), Map.class));
            prop.setExt(ONode.deserialize(prop.getExtJson(), Map.class));
            prop.setSwtJson(null);
            prop.setExtJson(null);
        }
        return list;
    }

    @Override
    public List<DataPropVO> selectSimpleList(DataPropVO dataProp) {
        return modelMapper.selectSimpleList(dataProp);
    }

    @Override
    public DataPropVO selectById(String uid) {
        DataProp dataProp = new DataProp();
        dataProp.setUid(uid);
        DataPropVO prop = modelMapper.selectEntity(dataProp);
        if (prop != null) {
            PropUtils.decodeJsonInfo(prop);
        }
        return prop;
    }

    @Override
    public String checkCodeUnique(DataPropVO dataProp) {
        String uid = ObjUtil.isNull(dataProp.getUid()) ? "" : dataProp.getUid();
        DataProp info = modelMapper.selectByCode(dataProp.getModelCode(), dataProp.getCode());
        if (ObjUtil.isNotNull(info) && !uid.equals(info.getUid())) {
            return UserConstants.NOT_UNIQUE;
        }
        return UserConstants.UNIQUE;
    }

    @Transaction
    @Override
    public int insert(DataPropVO dataProp) {
        dataProp.setUid(MelonUtils.snowId("P"));
        dataProp.setCreateTime(new Date());
        // JSON属性处理
        if (MapUtil.isNotEmpty(dataProp.getExt())) {
            dataProp.setExtJson(ONode.stringify(dataProp.getExt()));
        }
        if (MapUtil.isNotEmpty(dataProp.getSwt())) {
            dataProp.setSwtJson(ONode.stringify(dataProp.getSwt()));
        }
        return modelMapper.insert(dataProp);
    }

    @Override
    public int update(DataPropVO dataProp) {
        // JSON属性处理
        if (MapUtil.isNotEmpty(dataProp.getExt())) {
            dataProp.setExtJson(ONode.stringify(dataProp.getExt()));
        }
        if (MapUtil.isNotEmpty(dataProp.getSwt())) {
            dataProp.setSwtJson(ONode.stringify(dataProp.getSwt()));
        }
        return modelMapper.updateById(dataProp);
    }

    @Override
    public int updateEditFlag(DataPropVO dataProp) {
        return modelMapper.updateById(dataProp);
    }

    @Transaction
    @Override
    public int deleteById(String uid) {
        return modelMapper.deleteById(uid);
    }

    @Override
    public void uploadPDManFile(UploadVO uploadData) {
        ONode root = ONode.load(uploadData.getUploadJson());
        String expEntity = "$.entities[?(@.defKey == '" + uploadData.getTargetTableCode() + "')]";
        List<PDManTypeVO> typeList = root.select("$.dataTypeMapping.mappings").toObjectList(PDManTypeVO.class);
        List<PDManDomainVO> domainList = root.select("$.domains").toObjectList(PDManDomainVO.class);
        domainList.forEach(it -> typeList.stream()
                .filter(tp -> StrUtil.equals(tp.getId(), it.getApplyFor())).findFirst()
                .ifPresent((tp) -> it.setType(tp.getDefKey())));
        InputStream ins = Utils.getClassLoader().getResourceAsStream("init/prop_single.json");
        String initJson = IoUtil.readUtf8(ins);
        DataPropVO initProp = ONode.deserialize(initJson, DataPropVO.class);
        List<PDManTableVO> impList = root.select(expEntity).toObjectList(PDManTableVO.class);
        for (PDManTableVO table : impList) {
            for (PDManFieldVO field : table.getFields()) {
                DataPropVO impProp = new DataPropVO();
                BeanUtil.copyProperties(initProp, impProp);
                impProp.setModelCode(uploadData.getModelCode());
                impProp.setCreateBy(uploadData.getUserName());
                domainList.stream().filter(it -> StrUtil.equals(it.getId(), field.getDomain()))
                        .findFirst().ifPresent((it) -> buildTypeInfo(it, impProp, field));
                buildFromPDManField(impProp, field);
                if (UserConstants.NOT_UNIQUE.equals(this.checkCodeUnique(impProp))) {
                    continue;
                }
                log.debug("导入成功：{}", ONode.stringify(impProp));
                this.insert(impProp);
            }
        }
    }

    private void buildFromPDManField(DataPropVO impProp, PDManFieldVO field) {
        impProp.setUid(MelonUtils.snowId("P"));
        impProp.setCreateTime(new Date());
        impProp.setCode(field.getDefKey());
        impProp.setHideCode(null);
        impProp.setName(field.getDefName());
        // JSON属性处理
        if (MapUtil.isNotEmpty(impProp.getExt())) {
            impProp.setExtJson(ONode.stringify(impProp.getExt()));
        }
        if (MapUtil.isNotEmpty(impProp.getSwt())) {
            impProp.setSwtJson(ONode.stringify(impProp.getSwt()));
        }
    }

    private void buildTypeInfo(PDManDomainVO it, DataPropVO impProp, PDManFieldVO field) {
        Map<String, Object> extMap = impProp.getExt();
        // 设置导入类型
        if (StrUtil.equals(it.getType(), "double")) {
            impProp.setDataType(DataTypeEnum.NUMB.getVal());
            impProp.setViewType("B1");
            extMap.put("decLen", ObjUtil.defaultIfNull(field.getScale(), it.getScale()));
            extMap.put("maxLen", ObjUtil.defaultIfNull(field.getLen(), it.getLen()));
        } else if (StrUtil.equals(it.getType(), "int") || StrUtil.equals(it.getType(), "bigint")) {
            impProp.setDataType(DataTypeEnum.NUMB.getVal());
            impProp.setViewType("B0");
            extMap.put("maxLen", ObjUtil.defaultIfNull(field.getLen(), it.getLen()));
            extMap.put("decLen", 0);
        } else if (StrUtil.equals(it.getType(), "date")) {
            impProp.setDataType(DataTypeEnum.DATE.getVal());
        } else if (StrUtil.equals(it.getType(), "largeText") || StrUtil.equals(it.getType(), "bytes")) {
            impProp.setDataType(DataTypeEnum.FILE.getVal());
        } else {
            impProp.setDataType(DataTypeEnum.TEXT.getVal());
            extMap.put("maxLen", ObjUtil.defaultIfNull(field.getLen(), it.getLen()));
        }
    }
}
